package ar.edu.caece.compiler.grammar.factories;

import ar.edu.caece.compiler.error.ErrorManager;
import ar.edu.caece.compiler.error.ParsingException;
import ar.edu.caece.compiler.grammar.AbstractTokenVisitorFactory;
import ar.edu.caece.compiler.grammar.Recognizer;
import ar.edu.caece.compiler.grammar.TokenRecognizer;
import ar.edu.caece.compiler.grammar.TokenVisitor;
import ar.edu.caece.compiler.grammar.attributes.Attribute;
import ar.edu.caece.compiler.grammar.attributes.tree.NodeBloque;
import ar.edu.caece.compiler.grammar.attributes.tree.NodeDeclaraciones;
import ar.edu.caece.compiler.grammar.attributes.tree.NodeFuncProc;
import ar.edu.caece.compiler.grammar.nonterminals.ALTERP2;
import ar.edu.caece.compiler.grammar.nonterminals.LISTADECLARACIONES;
import ar.edu.caece.compiler.grammar.nonterminals.LISTAPROP;
import ar.edu.caece.compiler.grammar.nonterminals.Lambda;
import ar.edu.caece.compiler.grammar.terminals.ComenzarToken;
import ar.edu.caece.compiler.grammar.terminals.FinProcToken;
import ar.edu.caece.compiler.grammar.terminals.PuntoComaToken;

public class ALTERP2Factory extends AbstractTokenVisitorFactory {

	private static ALTERP2Factory INSTANCE;
	
	private LISTADECLARACIONESFactory listadeclaracionesFactory;
	private LISTAPROPFactory listapropFactory;

	private TokenVisitor parseLISTADECLARACIONES;
	private TokenVisitor parseLISTAPROP;
	private TokenVisitor parseComenzarToken;
	private TokenVisitor parseFinProcToken;
	private TokenVisitor parsePuntoComaToken;
	
	public static ALTERP2Factory getInstance() throws Exception {
		if(INSTANCE==null) INSTANCE = new ALTERP2Factory();
		return INSTANCE;
	}
	
	private Initializer initializer = new Initializer() {
		
		@Override
		public void init() throws Exception {
			initializer = NoOperationInitializer.getInstance();
			listadeclaracionesFactory = LISTADECLARACIONESFactory.getInstance();
			listapropFactory = LISTAPROPFactory.getInstance();
			parseLISTAPROP = listapropFactory.makeVisitor();
			parseLISTADECLARACIONES = listadeclaracionesFactory.makeVisitor();
			
			parseComenzarToken = new ComenzarToken.ComenzarTokenVisitor() {
				
				@Override
				public Recognizer defaultCase(TokenRecognizer llamador) throws Exception {
					ErrorManager.getInstance().manage(llamador, "comenzar", null);
					return null;
				}
				
				@Override
				public Recognizer comenzarCase(ComenzarToken llamador) {
					return llamador;
				}
			};
			
			parseFinProcToken = new FinProcToken.FinProcTokenVisitor() {
				
				@Override
				public Recognizer defaultCase(TokenRecognizer llamador) throws Exception {
					ErrorManager.getInstance().manage(llamador,"FinProcToken",new ParsingException("Error de parseo. Se recibi�: " +llamador.getToken(),FinProcToken.class,llamador));
					return null;
				}
				
				@Override
				public Recognizer finProcCase(FinProcToken llamador) {
					return llamador;
				}
			};
			parsePuntoComaToken = new PuntoComaToken.PuntoComaTokenVisitor() {
				
				@Override
				public Recognizer defaultCase(TokenRecognizer llamador) throws Exception {
					ErrorManager.getInstance().manage(llamador,"PuntoComaToken",new ParsingException("Error de parseo. Se recibi�: " +llamador.getToken(),PuntoComaToken.class,llamador));
					return null;
				}
				
				@Override
				public Recognizer puntoComaCase(PuntoComaToken llamador) {
					return llamador;
				}
			};
		}
	};
	
	private ALTERP2Factory() throws Exception {

	}
	
	@Override
	public TokenVisitor makeChainedVisitor(
			final TokenVisitor siguiente) throws Exception {
		initializer.init();

		return new TokenVisitor() {

			@Override
			public Recognizer defaultCase(TokenRecognizer llamador)
					throws Exception {

				System.out.println( "ALTERP2 default: " + getAttribute(Attribute.ARBOL_SINTETIZADO) );
				
				TokenVisitor parseLISTADECLARACIONES2 = listadeclaracionesFactory.makeChainedVisitor(siguiente);
				parseLISTADECLARACIONES2.addInheritedAttribute(Attribute.TABLA_SIMBOLOS_HEREDADA,getAttribute(Attribute.TABLA_SIMBOLOS_HEREDADA));
				parseLISTADECLARACIONES2.addInheritedAttribute(Attribute.ARBOL_HEREDADO, getAttribute(Attribute.ARBOL_HEREDADO));
				Recognizer result = llamador.execute(parseLISTADECLARACIONES2);
				if(result instanceof LISTADECLARACIONES) {
					
					LISTADECLARACIONES listadeclaraciones = (LISTADECLARACIONES) result;
					
					parseLISTAPROP.addInheritedAttribute(Attribute.TABLA_SIMBOLOS_HEREDADA,listadeclaraciones.getAttribute(Attribute.TABLA_SIMBOLOS_SINTETIZADA));

					
					ComenzarToken comenzarToken = (ComenzarToken) nextToken().execute(parseComenzarToken);
					
					parseLISTAPROP.addInheritedAttribute(Attribute.ARBOL_HEREDADO, new NodeBloque());

					LISTAPROP listaprop = (LISTAPROP) nextToken().execute(parseLISTAPROP);
					FinProcToken finProcToken = (FinProcToken) nextToken().execute(parseFinProcToken);
					PuntoComaToken puntoComaToken = (PuntoComaToken) nextToken().execute(parsePuntoComaToken);
					ALTERP2 alterp2 = new ALTERP2 (listadeclaraciones, comenzarToken, listaprop,finProcToken, puntoComaToken);
					
					alterp2.addAttribute(Attribute.TABLA_SIMBOLOS_SINTETIZADA, listadeclaraciones.getAttribute(Attribute.TABLA_SIMBOLOS_SINTETIZADA));

					NodeDeclaraciones declaraciones = listadeclaraciones instanceof Lambda?new NodeDeclaraciones():(NodeDeclaraciones) listadeclaraciones.getAttribute(Attribute.ARBOL_SINTETIZADO); 
					
					NodeFuncProc nodeFuncProc = (NodeFuncProc) getAttribute(Attribute.ARBOL_HEREDADO);
					nodeFuncProc.setDeclaraciones(declaraciones);
					nodeFuncProc.propagarLabel();
					
					nodeFuncProc.setBloque((NodeBloque) listaprop.getAttribute(Attribute.ARBOL_SINTETIZADO));
					
					alterp2.addAttribute(Attribute.ARBOL_SINTETIZADO, nodeFuncProc);
					return alterp2; 
				} 
				else {
					return result;
				}
			}
			
		};
	
	}
	@Override
	public TokenVisitor makeVisitor() throws Exception {
		initializer.init();

		return new TokenVisitor() {

			@Override
			public Recognizer defaultCase(TokenRecognizer llamador) throws Exception {
				
				System.out.println( "ALTERP2: " + getAttribute(Attribute.ARBOL_SINTETIZADO) );
				
				parseLISTADECLARACIONES.addInheritedAttribute(Attribute.TABLA_SIMBOLOS_HEREDADA,getAttribute(Attribute.TABLA_SIMBOLOS_HEREDADA));
				parseLISTADECLARACIONES.addInheritedAttribute(Attribute.ARBOL_HEREDADO, getAttribute(Attribute.ARBOL_HEREDADO));
				
				NodeFuncProc nodeFuncProc = (NodeFuncProc) getAttribute(Attribute.ARBOL_HEREDADO);

				LISTADECLARACIONES listadeclaraciones = (LISTADECLARACIONES) llamador.execute(parseLISTADECLARACIONES);
				
				parseLISTAPROP.addInheritedAttribute(Attribute.TABLA_SIMBOLOS_HEREDADA,listadeclaraciones.getAttribute(Attribute.TABLA_SIMBOLOS_SINTETIZADA));

				ComenzarToken comenzarToken = (ComenzarToken) nextToken().execute(parseComenzarToken);
				
				parseLISTAPROP.addInheritedAttribute(Attribute.ARBOL_HEREDADO, new NodeBloque());

				
				LISTAPROP listaprop = (LISTAPROP) nextToken().execute(parseLISTAPROP);
				FinProcToken finProcToken = (FinProcToken) nextToken().execute(parseFinProcToken);
				PuntoComaToken puntoComaToken = (PuntoComaToken) nextToken().execute(parsePuntoComaToken);
				ALTERP2 alterp2 = new ALTERP2 (listadeclaraciones, comenzarToken, listaprop,finProcToken, puntoComaToken);
				
				alterp2.addAttribute(Attribute.TABLA_SIMBOLOS_SINTETIZADA, listadeclaraciones.getAttribute(Attribute.TABLA_SIMBOLOS_SINTETIZADA));

				NodeDeclaraciones declaraciones = listadeclaraciones instanceof Lambda?new NodeDeclaraciones():(NodeDeclaraciones) listadeclaraciones.getAttribute(Attribute.ARBOL_SINTETIZADO); 
				
				nodeFuncProc.setDeclaraciones(declaraciones);
				nodeFuncProc.propagarLabel();
				
				nodeFuncProc.setBloque((NodeBloque) listaprop.getAttribute(Attribute.ARBOL_SINTETIZADO));
				
				alterp2.addAttribute(Attribute.ARBOL_SINTETIZADO, nodeFuncProc);
				return alterp2; 
			}
		};
	}
	
}
